# -*- coding:utf-8 -*-
from scipy import signal

from utils.csi_data import CSI_Data

import numpy as np


class CSI_Seq:
    def __init__(self, value, time):
        self.value = value
        self.time = time


class CSI_Detail:
    def __init__(self):
        self.totalCnt = 0
        self.totalTime = 0
        self.amplitude = []
        self.oriphase = []
        self.amplitudedif = []
        self.phasedif = []
        self.phase = []
        self.times = []


def getDetail(data):
    """

    :param data: CSI_Data list
    :return:

    Warning: This method may take long time,
    consider open a new thread to do this in order to prevent blocking the ui thread
    """
    detail = CSI_Detail()

    cnt = len(data)
    detail.totalCnt = cnt
    end = data[cnt - 1]  # type:CSI_Data
    start = data[0]  # type:CSI_Data
    detail.totalTime = (end.timestamp_low - start.timestamp_low) / 1e6
    detail.amplitude = [[[[] for _ in range(30)] for _ in range(3)] for _ in range(3)]
    detail.phase = [[[[] for _ in range(30)] for _ in range(3)] for _ in range(3)]
    detail.oriphase = [[[[] for _ in range(30)] for _ in range(3)] for _ in range(3)]
    for i in range(cnt):
        d = data[i]  # type:CSI_Data
        time = (d.timestamp_low - start.timestamp_low) / 1e6
        detail.times.append(time)
        for j in range(d.Ntx):
            for k in range(d.Nrx):
                phases = np.angle(d.csi[j][k])
                phases2 = phase_linear_trans(phases)
                for m in range(30):
                    amp = np.abs(d.csi[j][k][m])
                    seq = CSI_Seq(amp, time)
                    detail.amplitude[j][k][m].append(seq)
                    seq = CSI_Seq(phases[m], time)
                    detail.oriphase[j][k][m].append(seq)
                    seq = CSI_Seq(phases2[m], time)
                    detail.phase[j][k][m].append(seq)

    # 滤波

    for j in range(3):
        for k in range(3):
            for m in range(30):
                tmp = []
                tmp2 = []
                l = len(detail.amplitude[j][k][m])
                if l > 7:
                    for i in range(l):
                        tmp2.append(detail.amplitude[j][k][m][i].time)
                        tmp.append(detail.amplitude[j][k][m][i].value)
                    tmp = Hampel(tmp)
                    detail.amplitude[j][k][m].clear()
                    for i in range(len(tmp)):
                        detail.amplitude[j][k][m].append(CSI_Seq(tmp[i], tmp2[i + 7]))

                tmp = []
                tmp2 = []
                l = len(detail.phase[j][k][m])
                if l > 7:
                    for i in range(l):
                        tmp2.append(detail.phase[j][k][m][i].time)
                        tmp.append(detail.phase[j][k][m][i].value)
                    tmp = Hampel(tmp)
                    detail.phase[j][k][m].clear()
                    for i in range(len(tmp)):
                        detail.phase[j][k][m].append(CSI_Seq(tmp[i], tmp2[i + 7]))

    # 计算幅值差
    detail.amplitudedif = [[[[[] for _ in range(30)] for _ in range(3)] for _ in range(3)] for _ in range(3)]

    for j in range(3):
        for k in range(3):
            for n in range(3):
                for m in range(30):
                    d1 = detail.amplitude[j][k][m]
                    d2 = detail.amplitude[j][n][m]
                    tmp = []
                    if len(d1) == len(d2) and len(d1) != 0:
                        for i in range(len(d1)):
                            t = d1[i].time
                            v = d1[i].value - d2[i].value
                            tmp.append(CSI_Seq(v, t))
                    detail.amplitudedif[j][k][n][m] = tmp

    # 计算相位差
    detail.phasedif = [[[[[] for _ in range(30)] for _ in range(3)] for _ in range(3)] for _ in range(3)]

    for j in range(3):
        for k in range(3):
            for n in range(3):
                for m in range(30):
                    d1 = detail.oriphase[j][k][m]
                    d2 = detail.oriphase[j][n][m]
                    tmp = []
                    if len(d1) == len(d2) and len(d1) != 0:
                        for i in range(len(d1)):
                            t = d1[i].time
                            v = get_phase_dif_single(d1[i].value, d2[i].value)
                            tmp.append(CSI_Seq(v, t))
                    detail.phasedif[j][k][n][m] = tmp

    for j in range(3):
        for p in range(3):
            for k in range(3):
                for m in range(30):
                    tmp = []
                    tmp2 = []
                    l = len(detail.phasedif[j][p][k][m])
                    if l > 7:
                        for i in range(l):
                            tmp2.append(detail.phasedif[j][p][k][m][i].time)
                            tmp.append(detail.phasedif[j][p][k][m][i].value)
                        tmp = Hampel(tmp)
                        detail.phasedif[j][p][k][m].clear()
                        for i in range(len(tmp)):
                            detail.phasedif[j][p][k][m].append(CSI_Seq(tmp[i], tmp2[i + 7]))

    return detail


# 计算相位差
def get_phase_dif(csi1, csi2):
    csi_t = [0] * 30
    for i in range(30):
        if csi1[i] >= 0 and csi2[i] >= 0:
            if csi1[i] >= csi2[i]:
                temp = csi1[i] - csi2[i]
            else:
                temp = csi2[i] - csi1[i]
        elif csi1[i] > 0 and csi2[i] < 0:
            t = csi1[i] - np.pi
            if csi2[i] > t:
                temp = np.pi - csi2[i] + t
            else:
                temp = np.pi * 2 + csi2[i] - csi1[i]
        elif csi1[i] < 0 and csi2[i] > 0:
            t = csi2[i] - np.pi
            if csi1[i] >= t:
                temp = np.pi - csi1[i] + t
            else:
                temp = np.pi * 2 + csi1[i] - csi2[i]
        else:
            if csi1[i] >= csi2[i]:
                temp = csi1[i] - csi2[i]
            else:
                temp = csi2[i] - csi1[i]

        csi_t[i] = temp

    return csi_t


# 计算相位差
def get_phase_dif_single(csi1, csi2):
    if csi1 >= 0 and csi2 >= 0:
        if csi1 >= csi2:
            ret = csi1 - csi2
        else:
            ret = csi2 - csi1
    elif csi1 > 0 and csi2 < 0:
        t = csi1 - np.pi
        if csi2 > t:
            ret = np.pi - csi2 + t
        else:
            ret = np.pi * 2 + csi2 - csi1
    elif csi1 < 0 and csi2 > 0:
        t = csi2 - np.pi
        if csi1 >= t:
            ret = np.pi - csi1 + t
        else:
            ret = np.pi * 2 + csi1 - csi2
    else:
        if csi1 >= csi2:
            ret = csi1 - csi2
        else:
            ret = csi2 - csi1

    return ret


# 相位拓展
def phase_expend(dat):
    csi_pha_exp = [0] * 30
    csi_pha_exp[0] = dat[0]
    for i in range(1, 30):
        if (dat[i] - dat[i - 1]) >= np.pi:
            csi_pha_exp[i] = csi_pha_exp[i - 1] + (dat[i] - dat[i - 1] - 2 * np.pi)
        elif (dat[i] - dat[i - 1]) <= -np.pi:
            csi_pha_exp[i] = csi_pha_exp[i - 1] + (dat[i] - dat[i - 1] + 2 * np.pi)
        else:
            csi_pha_exp[i] = csi_pha_exp[i - 1] + (dat[i] - dat[i - 1])
    return csi_pha_exp


def phase_linear_trans(dat):
    csi_pha_expend = phase_expend(dat)
    csi_pha_trans = [0] * 30
    m = [-28, -26, -24, -22, -20, -18, -16, -14, -12, -10, -8, -6, -4, -2, -1, 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21,
         23, 25, 27, 28]

    k = (csi_pha_expend[29] - csi_pha_expend[0]) / (m[29] - m[0])  # 斜率
    b = np.sum(csi_pha_expend) / 30  # 截距

    for i in range(30):
        csi_pha_trans[i] = csi_pha_expend[i] - k * m[i] - b

    return csi_pha_trans


def Hampel(z):
    frame = 5  # hample滤波器的窗口至少为7
    L = 3
    z_ham = []
    for i in range(frame, len(z[frame:-frame])):
        l = z[i - frame:i + frame]
        d = np.median(l)
        s = l - d
        m = np.median(s)
        MAD = 1.4826 * m
        if abs(l[0] - d) < L * MAD:
            z_ham.append(l[0])
        else:
            z_ham.append(d)
    s = np.arange(len(z_ham))
    b, a = signal.butter(3, 0.03, 'low')
    sf = signal.filtfilt(b, a, z_ham)
    z_ham = sf
    return z_ham
